<?php
/**
 * Description:
 * Author: hezhizheng
 * Date: 2018/8/14
 * Time: 16:45
 * Created by Created by Panxsoft.
 */

namespace App\Services\Repositories\SkinRecord;

use App\Models\Customer\Customer;
use App\Models\Customer\DeviceAccount;
use App\Models\Store\SkinRecord;
use App\Models\Store\Store;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\DB;

/**
 *
 * Class SkinRecordCountRepository
 * @package App\Services\Repositories\SkinRecord
 */
class SkinRecordCountRepository
{
    public function storeCountSkin(Customer $customer)
    {
        $store_ids = $customer->stores()->pluck('id')->all();

        $created_ats = $this->formatSkinRecordCreatedAt($store_ids);

        $getStoreQueryGroupByArray = $this->storeQuery($store_ids)
            ->groupBy('store_id')
            ->toArray();

        $store_count_skin = $this->formatStoreQueryGroupByArray($getStoreQueryGroupByArray, $created_ats);

        return $store_count_skin;
    }

    private function countStoreData(Store $store, $created_ats)
    {
        $data = [];
        $group_type = $this->getGroupType();
        foreach ($created_ats as $created_at) {
            $count = $this->countStoreSkinRecord($store, $group_type, $created_at);
            $data [] = [
                'date'  => $created_at,
                'count' => $count,
            ];
        }
        return $data;
    }

    public function deviceAccountCountSkin(array $store_ids)
    {
        $device_accounts = (new DeviceAccount)
            ->whereIn('store_id', $store_ids)
            ->select(['id', 'device_name', 'store_id'])
            ->get();

        $created_ats = $this->formatSkinRecordCreatedAt($store_ids);

        $getDeviceAccountQueryGroupByArray = $this->deviceAccountQuery($device_accounts->pluck('id'))
            ->groupBy(['device_account_id',])
            ->toArray();

        $device_account_count_skin = $this->formatDeviceAccountQueryGroupByArray($getDeviceAccountQueryGroupByArray, $created_ats);

        return $device_account_count_skin;
    }

    private function countSkinAppendDiffDate(array $count_skin, array $created_ats)
    {
        $collect_created_ats = collect($created_ats);

        $diff = $collect_created_ats->diff(collect($count_skin)->pluck('date'));

        $append_data = [];
        foreach ($diff as $date) {
            $append_data[] = [
                'date'  => $date,
                'count' => 0,
            ];
        }
        return collect(array_merge($count_skin, $append_data))->sortBy('date')->values()->all();
    }

    private function countDeviceAccountData(DeviceAccount $deviceAccount, $created_ats)
    {
        $data = [];
        $group_type = $this->getGroupType();
        foreach ($created_ats as $created_at) {
            $count = $this->countDeviceAccountSkinRecord($deviceAccount, $group_type, $created_at);
            $data [] = [
                'date'  => $created_at,
                'count' => $count,
            ];
        }

        return $data;
    }

    /**
     * @param array $store_ids
     * @return array
     */
    private function formatSkinRecordCreatedAt(array $store_ids): array
    {
        $created_ats = (new SkinRecord)
            ->whereIn('store_id', $store_ids)
            ->orderBy('created_at', 'ASC')
            ->pluck('created_at')
            ->all();

        $group_type = $this->getGroupType();
        $created_ats = array_map(function (Carbon $created_at) use ($group_type) {
            switch ($group_type) {
                case 'year' :
                    return $this->formatYDate($created_at->toDateString());

                case 'day' :
                    return $created_at->toDateString();

                case 'month' :
                default :
                    return $this->formatMDate($created_at->toDateString());
            }
        }, $created_ats);

        $created_ats = collect($created_ats)->unique()->values()->all();
        return $created_ats;
    }

    /**
     * @param Store $store
     * @param $group_type
     * @param $created_at
     * @return int
     */
    private function countStoreSkinRecord(Store $store, $group_type, $created_at): int
    {
        switch ($group_type) {
            case 'year':
                $count = $store->skinRecords()->whereYear('created_at', $created_at)->count();
                break;
            case 'day':
                $count = $store->skinRecords()->whereDate('created_at', $created_at)->count();
                break;
            default : // month
                $at = explode('-', $created_at);
                $year = $at[0];
                $moth = $at[1];
                $count = $store->skinRecords()->whereYear('created_at', $year)->whereMonth('created_at', $moth)->count();
                break;
        }
        return $count;
    }

    /**
     * @param DeviceAccount $deviceAccount
     * @param $group_type
     * @param $created_at
     * @return int
     */
    private function countDeviceAccountSkinRecord(DeviceAccount $deviceAccount, $group_type, $created_at)
    {
        switch ($group_type) {
            case 'year':
                $count = $deviceAccount->skinRecords()->whereYear('created_at', $created_at)->count();
                break;
            case 'day':
                $count = $deviceAccount->skinRecords()->whereDate('created_at', $created_at)->count();
                break;
            default : // month
                $at = explode('-', $created_at);
                $year = $at[0];
                $moth = $at[1];
                $count = $deviceAccount->skinRecords()->whereYear('created_at', $year)->whereMonth('created_at', $moth)->count();
                break;
        }
        return $count;
    }

    private function formatMDate(string $created_at)
    {
        $at = explode('-', $created_at);
        return $at[0] . '-' . $at[1];
    }

    private function formatYDate(string $created_at)
    {
        $at = explode('-', $created_at);
        return $at[0];
    }

    /**
     * @return string|null
     */
    private function getGroupType(): ? string
    {
        $group_type = request()->group_type;
        return $group_type;
    }


    private function deviceAccountQuery($device_account_ids)
    {
        $groupSqlByType = $this->groupSqlByType($this->getGroupType());

        // 这里只会出来有测肤数据的设备
        $query = (new SkinRecord)
            ->select([
                'device_account_id',
                DB::raw('COUNT(*) AS count'),
                DB::raw($groupSqlByType['format_at']),
                DB::raw($groupSqlByType['select'])
            ])
            ->with(['deviceAccount' => function (BelongsTo $query) {
                $query->select(['id', 'device_name']);
            }])
            ->whereIn('device_account_id', $device_account_ids)
            ->groupBy('device_account_id', $groupSqlByType['groupBy'])
            ->get();

        return $query;
    }

    private function storeQuery($store_ids)
    {
        $groupSqlByType = $this->groupSqlByType($this->getGroupType());

        // 这里只会出来有测肤数据的店铺
        $query = (new SkinRecord)
            ->select([
                'store_id',
                DB::raw('COUNT(*) AS count'),
                DB::raw($groupSqlByType['format_at']),
                DB::raw($groupSqlByType['select'])
            ])
            ->with(['store' => function (BelongsTo $query) {
                $query->select(['id', 'name']);
            }])
            ->whereIn('store_id', $store_ids)
            ->groupBy('store_id', $groupSqlByType['groupBy'])
            ->get();

        return $query;
    }

    private function groupSqlYearMonthDay(): array
    {
        return $sql = [
            'select'    => "YEAR(created_at) AS `year`, MONTH(created_at) AS `month`, DAY(created_at) AS `day`",
            'groupBy'   => ['year', 'month', 'day'],
            'format_at' => "date_format(MAX(created_at), '%Y-%m-%d') AS format_at",
        ];
    }

    private function groupSqlYearMonth(): array
    {
        return $sql = [
            'select'    => "YEAR(created_at) AS `year`, MONTH(created_at) AS `month`",
            'groupBy'   => ['year', 'month'],
            'format_at' => "date_format(MAX(created_at), '%Y-%m') AS format_at",
        ];
    }

    private function groupSqlYear(): array
    {
        return $sql = [
            'select'    => "YEAR(created_at) AS year", // year / month / week 不用 `` 也不会报错，什么情况使用关键字会报错呢？
            'groupBy'   => "year",
            'format_at' => "date_format(MAX(created_at), '%Y') AS format_at",
        ];
    }

    private function groupSqlByType(?string $group_type): array
    {
        switch (mb_strtolower($group_type)) {
            case 'year' :
                return $this->groupSqlYear();
            case 'month' :
                return $this->groupSqlYearMonth();
            case 'day' :
                return $this->groupSqlYearMonthDay();
            default :
                return $this->groupSqlYearMonth();
        }
    }

    /**
     * @param $getDeviceAccountQueryGroupByArray
     * @param $created_ats
     * @return array
     */
    private function formatDeviceAccountQueryGroupByArray($getDeviceAccountQueryGroupByArray, $created_ats): array
    {
        $device_account_count_skin = [];
        foreach ($getDeviceAccountQueryGroupByArray as $device_account_id => $device_account_date) {

            $count_skin = [];
            foreach ($device_account_date as $date_count) {
                $count_skin [] = [
                    "date"  => $date_count['format_at'],
                    "count" => $date_count['count']
                ];
            }

            $device_account_count_skin[] = [
                'id'          => $device_account_id,
                'device_name' => $device_account_date[0]['device_account']['device_name'],
                'count_skin'  => $this->countSkinAppendDiffDate($count_skin, $created_ats),
            ];
        }
        return $device_account_count_skin;
    }

    /**
     * @param $getStoreQueryGroupByArray
     * @param $created_ats
     * @return array
     */
    private function formatStoreQueryGroupByArray($getStoreQueryGroupByArray, $created_ats): array
    {
        $store_count_skin = [];
        foreach ($getStoreQueryGroupByArray as $store_id => $store_date) {

            $count_skin = [];
            foreach ($store_date as $date_count) {
                $count_skin [] = [
                    "date"  => $date_count['format_at'],
                    "count" => $date_count['count']
                ];
            }

            $store_count_skin[] = [
                'id'         => $store_id,
                'name'       => $store_date[0]['store']['name'],
                'count_skin' => $this->countSkinAppendDiffDate($count_skin, $created_ats),
            ];
        }
        return $store_count_skin;
    }


}